namespace cxxcall
{

bool av_data::call ()
{
  if (this->error ())
    return (false);
 
  register uintptr_t *sp __asm__ ("esp");

  ptrdiff_t arg_len = this->curp - this->args;
  if (arg_len > 0)
    {
      auto frame = (uintptr_t *)__builtin_alloca (arg_len * WSIZE);
      for (ptrdiff_t i = 0; i < arg_len; ++i)
        frame[i] = this->args[i];

      // Try to convince the compiler to *really* push the args into the stack.
      __asm__ __volatile__ ("movl %0, %%edx" : : "g" (frame));
    }

#define put_ret(Type)   \
  *(Type *)this->raddr = ((Type (*) ())this->func) ()

  if (this->rtype == type::float_t)
    put_ret (float);
  else if (this->rtype == type::double_t)
    put_ret (double);
  else if (this->rtype == type::ldouble_t)
    put_ret (long double);
  else
    {
      twoint x;
      x.ll = ((long long (*) ())this->func) ();

#undef put_ret
#define put_ret(Type)   \
  *(Type *)this->raddr = (Type)x.i.u[0];   \
  break

      switch (this->rtype)
        {
          case type::void_t:
            break;
          case type::schar_t:
            put_ret (signed char);
          case type::uchar_t:
            put_ret (unsigned char);
          case type::sshort_t:
            put_ret (short);
          case type::ushort_t:
            put_ret (unsigned short);
          case type::sint_t:
          case type::slong_t:
            put_ret (int);
          case type::uint_t:
          case type::ulong_t:
            put_ret (unsigned int);
          case type::sllong_t:
          case type::ullong_t:
            *(long long *)this->raddr = x.ll;
            break;
          case type::struct_t:
            if (!(this->flags & reg_struct_ret))
              break;
            {
              switch (this->rsize)
                {
                  case 1:
                    put_ret (unsigned char);
                  case 2:
                    put_ret (unsigned short);
                  case 4:
                    put_ret (unsigned int);
                  case 8:
                    *(long long *)this->raddr = x.ll;
                    break;
                }
            }
        }
    }

  return (true);
}

}
